/*
 * Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/>
 * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2016 MaNGOS <http://getmangos.com/>
 * Copyright (C) 2006-2014 ScriptDev2 <https://github.com/scriptdev2/scriptdev2/>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "azjol_nerub.h"

enum Spells
{
    SPELL_MIND_FLAY                               = 52586,
    SPELL_MIND_FLAY_H                             = 59367,
    SPELL_CURSE_OF_FATIGUE                        = 52592,
    SPELL_CURSE_OF_FATIGUE_H                      = 59368,
    SPELL_FRENZY                                  = 28747, //maybe 53361
    SPELL_SUMMON_SKITTERING_SWARMER               = 52438, //AOE Effect 140, maybe 52439
    SPELL_SUMMON_SKITTERING_SWARMER_1             = 52439, //Summon 3x 28735
    SPELL_ACID_SPLASH_H                           = 59363,
    SPELL_ACID_SPLASH                             = 52446,
    SPELL_CHARGE                                  = 16979, //maybe is another spell
    SPELL_BACKSTAB                                = 52540,
    SPELL_SHADOW_BOLT                             = 52534,
    SPELL_SHADOW_BOLT_H                           = 59357,
    SPELL_SHADOW_NOVA                             = 52535,
    SPELL_SHADOW_NOVA_H                           = 59358,
    SPELL_STRIKE                                  = 52532,
    SPELL_CLEAVE                                  = 49806,
    SPELL_ENRAGE                                  = 52470,
    SPELL_INFECTED_BITE                           = 52469,
    SPELL_INFECTED_BITE_H                         = 59364,
    SPELL_WEB_WRAP                                = 52086,
    SPELL_WEB_WRAP_STUN                           = 52087,
    SPELL_BLINDING_WEBS                           = 52524,
    SPELL_BLINDING_WEBS_H                         = 59365,
    SPELL_POSION_SPRAY                            = 52493,
    SPELL_POSION_SPRAY_H                          = 59366
};

enum Mobs
{
    NPC_SKITTERING_SWARMER                        = 28735,
    NPC_SKITTERING_SWARMER_CONTROLLER             = 32593,
    NPC_SKITTERING_INFECTIOR                      = 28736
};

enum Yells
{
    SAY_AGGRO                                     = 0,
    SAY_SLAY                                      = 1,
    SAY_DEATH                                     = 2,
    SAY_SWARM                                     = 3,
    SAY_PREFIGHT_1                                = 4,
    SAY_PREFIGHT_2                                = 5,
    SAY_PREFIGHT_3                                = 6,
    SAY_SEND_GROUP_1                              = 7,
    SAY_SEND_GROUP_2                              = 8,
    SAY_SEND_GROUP_3                              = 9,
};

enum
{
    WORLD_STATE_WATCH_HIM_DIE = 5818,
};


const Position SpawnPoint[] =
{
    { 566.164f, 682.087f, 769.079f, 2.21657f  },
    { 529.042f, 706.941f, 777.298f, 1.0821f   },
    { 489.975f, 671.239f, 772.131f, 0.261799f },
    { 488.556f, 692.95f,  771.764f, 4.88692f  },
    { 553.34f,  640.387f, 777.419f, 1.20428f  },
    { 517.486f, 706.398f, 777.335f, 5.35816f  },
    { 504.01f,  637.693f, 777.479f, 0.506145f },
    { 552.625f, 706.408f, 777.177f, 3.4383f   }
};

InstanceCreatures const Watchers[3] = { NPC_WATCHER_GASHRA, NPC_WATCHER_SILTHIK, NPC_WATCHER_NARJIL };

class boss_krik_thir : public CreatureScript
{
    public:
        boss_krik_thir() : CreatureScript("boss_krik_thir") { }

        struct boss_krik_thirAI : public ScriptedAI
        {
            boss_krik_thirAI(Creature* creature) : ScriptedAI(creature)
            {
                instance = creature->GetInstanceScript();
                me->GetMap()->SetWorldState(WORLD_STATE_WATCH_HIM_DIE, 1);
            }

            InstanceScript* instance;

            uint32 uiMindFlayTimer;
            uint32 uiCurseFatigueTimer;
            uint32 uiSummonTimer;

            void Reset() override
            {
                uiMindFlayTimer = 15 * IN_MILLISECONDS;
                uiCurseFatigueTimer = 12 * IN_MILLISECONDS;

                if (instance)
                    instance->SetData(DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, NOT_STARTED);
            }

            void EnterCombat(Unit* who) override
            {
                Talk(SAY_AGGRO);
                Summon();
                uiSummonTimer = 15 * IN_MILLISECONDS;

                for (InstanceCreatures watcherEntry : Watchers)
                    if (Creature* watcher = me->FindNearestCreature(watcherEntry, 50.0f))
                        if (!watcher->IsInCombat() && watcher->IsAIEnabled)
                            watcher->AI()->AttackStart(who);

                if (instance)
                    instance->SetData(DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, IN_PROGRESS);
            }

            void Summon()
            {
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[0], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[0], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[1], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[1], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[2], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[2], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[3], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[3], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_INFECTIOR, SpawnPoint[4], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[4], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_INFECTIOR, SpawnPoint[5], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[5], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_INFECTIOR, SpawnPoint[6], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[6], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[7], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
                me->SummonCreature(NPC_SKITTERING_SWARMER,   SpawnPoint[7], TEMPSUMMON_TIMED_DESPAWN, 25 * IN_MILLISECONDS);
            }

            void UpdateAI(uint32 diff) override
            {
                if (!UpdateVictim())
                    return;

                if (uiSummonTimer <= diff)
                {
                    Summon();
                    uiSummonTimer = 15 * IN_MILLISECONDS;
                } else uiSummonTimer -= diff;

                if (uiMindFlayTimer <= diff)
                {
                        DoCast(me->GetVictim(), SPELL_MIND_FLAY);
                        uiMindFlayTimer = 15 * IN_MILLISECONDS;
                    } else uiMindFlayTimer -= diff;

                if (uiCurseFatigueTimer <= diff)
                {
                    // WowWiki say "Curse of Fatigue-Kirk'thir will cast Curse of Fatigue on 2-3 targets periodically."
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
                        DoCast(target, SPELL_CURSE_OF_FATIGUE);
                    if (Unit* tankTarget = SelectTarget(SELECT_TARGET_RANDOM, 1, 100, true))
                        DoCast(tankTarget, SPELL_CURSE_OF_FATIGUE);

                    uiCurseFatigueTimer = 10 * IN_MILLISECONDS;
                } else uiCurseFatigueTimer -= diff;

                if (!me->HasAura(SPELL_FRENZY) && HealthBelowPct(10))
                    DoCast(me, SPELL_FRENZY, true);

                DoMeleeAttackIfReady();
            }

            void JustDied(Unit* /*killer*/) override
            {
                Talk(SAY_DEATH);

                if (instance)
                    instance->SetData(DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, DONE);
            }

            void KilledUnit(Unit* victim) override
            {
                if (victim->GetTypeId() != TYPEID_PLAYER)
                    return;

                Talk(SAY_SLAY);
            }

            void JustSummoned(Creature* summon) override
            {
                summon->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
            }
        };

        CreatureAI* GetAI(Creature* creature) const override
        {
            return new boss_krik_thirAI(creature);
        }
};

class npc_skittering_infector : public CreatureScript
{
    public:
        npc_skittering_infector() : CreatureScript("npc_skittering_infector") { }

        struct npc_skittering_infectorAI : public ScriptedAI
        {
            npc_skittering_infectorAI(Creature* creature) : ScriptedAI(creature) { }

            void JustDied(Unit* /*killer*/) override
            {
                // The spell is not working propperly
                DoCast(me->GetVictim(), SPELL_ACID_SPLASH, true);
            }
        };

        CreatureAI* GetAI(Creature* creature) const override
        {
            return new npc_skittering_infectorAI(creature);
        }
};

class npc_anub_ar_skirmisher : public CreatureScript
{
    public:
        npc_anub_ar_skirmisher() : CreatureScript("npc_anub_ar_skirmisher") { }

        struct npc_anub_ar_skirmisherAI : public ScriptedAI
        {
            npc_anub_ar_skirmisherAI(Creature* creature) : ScriptedAI(creature) { }

            uint32 uiChargeTimer;
            uint32 uiBackstabTimer;

            void Reset() override
            {
                uiChargeTimer   = 11 * IN_MILLISECONDS;
                uiBackstabTimer = 7 * IN_MILLISECONDS;
            }

            void UpdateAI(uint32 diff) override
            {
                if (!UpdateVictim())
                    return;

                if (uiChargeTimer <= diff)
                {
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
                    {
                        DoResetThreat();
                        me->AddThreat(target, 1.0f);
                        DoCast(target, SPELL_CHARGE, true);
                    }
                    uiChargeTimer = 15 * IN_MILLISECONDS;
                } else uiChargeTimer -= diff;

                if (uiBackstabTimer <= diff)
                {
                    DoCast(me->GetVictim(), SPELL_BACKSTAB);
                    uiBackstabTimer = 12 * IN_MILLISECONDS;
                } else uiBackstabTimer -= diff;

                DoMeleeAttackIfReady();

            }
        };

        CreatureAI* GetAI(Creature* creature) const override
        {
            return new npc_anub_ar_skirmisherAI(creature);
        }
};

class npc_anub_ar_shadowcaster : public CreatureScript
{
    public:
        npc_anub_ar_shadowcaster() : CreatureScript("npc_anub_ar_shadowcaster") { }

        struct npc_anub_ar_shadowcasterAI : public ScriptedAI
        {
            npc_anub_ar_shadowcasterAI(Creature* creature) : ScriptedAI(creature) { }

            uint32 uiShadowBoltTimer;
            uint32 uiShadowNovaTimer;

            void Reset() override
            {
                uiShadowBoltTimer = 6 * IN_MILLISECONDS;
                uiShadowNovaTimer = 15 * IN_MILLISECONDS;
            }

            void UpdateAI(uint32 diff) override
            {
                if (!UpdateVictim())
                    return;

                if (uiShadowBoltTimer <= diff)
                {
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
                         DoCast(target, SPELL_SHADOW_BOLT, true);
                    uiShadowBoltTimer = 15 * IN_MILLISECONDS;
                } else uiShadowBoltTimer -= diff;

                if (uiShadowNovaTimer <= diff)
                {
                    DoCast(me->GetVictim(), SPELL_SHADOW_NOVA, true);
                    uiShadowNovaTimer = 17 * IN_MILLISECONDS;
                } else uiShadowNovaTimer -= diff;

                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const override
        {
            return new npc_anub_ar_shadowcasterAI(creature);
        }
};

class npc_anub_ar_warrior : public CreatureScript
{
    public:
        npc_anub_ar_warrior() : CreatureScript("npc_anub_ar_warrior") { }

        struct npc_anub_ar_warriorAI : public ScriptedAI
        {
            npc_anub_ar_warriorAI(Creature* creature) : ScriptedAI(creature){ }

            uint32 uiCleaveTimer;
            uint32 uiStrikeTimer;

            void Reset() override
            {
                uiCleaveTimer = 11 * IN_MILLISECONDS;
                uiStrikeTimer = 6 * IN_MILLISECONDS;
            }

            void UpdateAI(uint32 diff) override
            {
                if (!UpdateVictim())
                    return;

                if (uiStrikeTimer <= diff)
                {
                    DoCast(me->GetVictim(), SPELL_STRIKE, true);
                    uiStrikeTimer = 15 * IN_MILLISECONDS;
                } else uiStrikeTimer -= diff;

                if (uiCleaveTimer <= diff)
                {
                    DoCast(me->GetVictim(), SPELL_CLEAVE, true);
                    uiCleaveTimer = 17 * IN_MILLISECONDS;
                } else uiCleaveTimer -= diff;

                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const override
        {
            return new npc_anub_ar_warriorAI(creature);
        }
};

class npc_watcher_gashra : public CreatureScript
{
    public:
        npc_watcher_gashra() : CreatureScript("npc_watcher_gashra") { }

        struct npc_watcher_gashraAI : public ScriptedAI
        {
            npc_watcher_gashraAI(Creature* creature) : ScriptedAI(creature) { }

            uint32 uiWebWrapTimer;
            uint32 uiInfectedBiteTimer;

            void Reset() override
            {
                uiWebWrapTimer      = 11 * IN_MILLISECONDS;
                uiInfectedBiteTimer = 4 * IN_MILLISECONDS;
            }

            void EnterCombat(Unit* /*who*/) override
            {
                DoCast(me, SPELL_ENRAGE, true);
            }

            void JustDied(Unit* /*killer*/) override
            {
                me->GetMap()->SetWorldState(WORLD_STATE_WATCH_HIM_DIE, 0);
            }

            void UpdateAI(uint32 diff) override
            {
                if (!UpdateVictim())
                    return;

                if (uiWebWrapTimer <= diff)
                {
                    if (me->HasUnitState(UNIT_STATE_CASTING))
                        return;
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit* t) { return t->GetTypeId() == TYPEID_PLAYER && me->IsWithinCombatRange(t, 40.0f) && !t->HasAura(SPELL_WEB_WRAP) && !t->HasAura(SPELL_WEB_WRAP_STUN); }))
                        DoCast(target, SPELL_WEB_WRAP);
                    uiWebWrapTimer = 17 * IN_MILLISECONDS;
                } else uiWebWrapTimer -= diff;

                if (uiInfectedBiteTimer <= diff)
                {
                    if (me->HasUnitState(UNIT_STATE_CASTING))
                        return;
                    DoCast(me->GetVictim(), SPELL_INFECTED_BITE);
                    uiInfectedBiteTimer = 15 * IN_MILLISECONDS;
                } else uiInfectedBiteTimer -= diff;

                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const override
        {
            return new npc_watcher_gashraAI(creature);
        }
};

class npc_watcher_narjil : public CreatureScript
{
    public:
        npc_watcher_narjil() : CreatureScript("npc_watcher_narjil") { }

        struct npc_watcher_narjilAI : public ScriptedAI
        {
            npc_watcher_narjilAI(Creature* creature) : ScriptedAI(creature) { }

            uint32 uiWebWrapTimer;
            uint32 uiInfectedBiteTimer;
            uint32 uiBindingWebsTimer;

            void Reset() override
            {
                uiWebWrapTimer      = 11 * IN_MILLISECONDS;
                uiInfectedBiteTimer = 4 * IN_MILLISECONDS;
                uiBindingWebsTimer = 17 * IN_MILLISECONDS;
            }

            void JustDied(Unit* /*killer*/) override
            {
                me->GetMap()->SetWorldState(WORLD_STATE_WATCH_HIM_DIE, 0);
            }

            void UpdateAI(uint32 diff) override
            {
                if (!UpdateVictim())
                    return;

                if (uiWebWrapTimer <= diff)
                {
                    if (me->HasUnitState(UNIT_STATE_CASTING))
                        return;
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit* t) { return t->GetTypeId() == TYPEID_PLAYER && me->IsWithinCombatRange(t, 40.0f) && !t->HasAura(SPELL_WEB_WRAP) && !t->HasAura(SPELL_WEB_WRAP_STUN); }))
                        DoCast(target, SPELL_WEB_WRAP);
                    uiWebWrapTimer = 15 * IN_MILLISECONDS;
                } else uiWebWrapTimer -= diff;

                if (uiInfectedBiteTimer <= diff)
                {
                    if (me->HasUnitState(UNIT_STATE_CASTING))
                        return;
                    DoCast(me->GetVictim(), SPELL_INFECTED_BITE);
                    uiInfectedBiteTimer = 11 * IN_MILLISECONDS;
                } else uiInfectedBiteTimer -= diff;

                if (uiBindingWebsTimer <= diff)
                {
                    if (me->HasUnitState(UNIT_STATE_CASTING))
                        return;
                    DoCast(me->GetVictim(), SPELL_BLINDING_WEBS);
                    uiBindingWebsTimer = 17 * IN_MILLISECONDS;
                } else uiBindingWebsTimer -= diff;

                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const override
        {
            return new npc_watcher_narjilAI(creature);
        }
};

class npc_watcher_silthik : public CreatureScript
{
    public:
        npc_watcher_silthik() : CreatureScript("npc_watcher_silthik") { }

        struct npc_watcher_silthikAI : public ScriptedAI
        {
            npc_watcher_silthikAI(Creature* creature) : ScriptedAI(creature) { }

            uint32 uiWebWrapTimer;
            uint32 uiInfectedBiteTimer;
            uint32 uiPoisonSprayTimer;

            void Reset() override
            {
                uiWebWrapTimer      = 11 * IN_MILLISECONDS;
                uiInfectedBiteTimer = 4 * IN_MILLISECONDS;
                uiPoisonSprayTimer  = 15 * IN_MILLISECONDS;
            }

            void JustDied(Unit* /*killer*/) override
            {
                me->GetMap()->SetWorldState(WORLD_STATE_WATCH_HIM_DIE, 0);
            }

            void UpdateAI(uint32 diff) override
            {
                if (!UpdateVictim())
                    return;

                if (uiWebWrapTimer <= diff)
                {
                    if (me->HasUnitState(UNIT_STATE_CASTING))
                        return;
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit* t) { return t->GetTypeId() == TYPEID_PLAYER && me->IsWithinCombatRange(t, 40.0f) && !t->HasAura(SPELL_WEB_WRAP) && !t->HasAura(SPELL_WEB_WRAP_STUN); }))
                        DoCast(target, SPELL_WEB_WRAP);

                    uiWebWrapTimer = 15 * IN_MILLISECONDS;
                } else uiWebWrapTimer -= diff;

                if (uiInfectedBiteTimer <= diff)
                {
                    if (me->HasUnitState(UNIT_STATE_CASTING))
                        return;
                    DoCast(me->GetVictim(), SPELL_INFECTED_BITE);
                    uiInfectedBiteTimer = 15 * IN_MILLISECONDS;
                } else uiInfectedBiteTimer -= diff;

                if (uiPoisonSprayTimer <= diff)
                {
                    if (me->HasUnitState(UNIT_STATE_CASTING))
                        return;
                    DoCast(me->GetVictim(), SPELL_POSION_SPRAY);
                    uiPoisonSprayTimer = 17 * IN_MILLISECONDS;
                } else uiPoisonSprayTimer -= diff;

                DoMeleeAttackIfReady();

            }
        };

        CreatureAI* GetAI(Creature* creature) const override
        {
            return new npc_watcher_silthikAI(creature);
        }
};

class spell_web_wrap : public SpellScriptLoader
{
    public:
        spell_web_wrap() : SpellScriptLoader("spell_web_wrap") { }

        class spell_web_wrap_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_web_wrap_AuraScript);

            void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
            {
                if (AuraApplication const* application = GetTargetApplication())
                    if (application->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
                        if (Unit* target = GetTarget())
                            target->CastSpell(target, SPELL_WEB_WRAP_STUN, true);
            }

            void Register() override
            {
                OnEffectRemove += AuraEffectRemoveFn(spell_web_wrap_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_MOD_ROOT, AURA_EFFECT_HANDLE_REAL);
            }
        };

        AuraScript* GetAuraScript() const override
        {
            return new spell_web_wrap_AuraScript();
        }
};

void AddSC_boss_krik_thir()
{
    new boss_krik_thir();
    new npc_skittering_infector();
    new npc_anub_ar_skirmisher();
    new npc_anub_ar_shadowcaster();
    new npc_watcher_gashra();
    new npc_anub_ar_warrior();
    new npc_watcher_silthik();
    new npc_watcher_narjil();
    new spell_web_wrap();
}
